#include "EMReflector.h"


void EMReflector::startReflectionDraw(const Camera& camera, const vector<Light*>& lights)
{
	
	// save current rtv/dsv
	context()->OMGetRenderTargets(1, &rtv, &dsv);

	// save vp
	UINT num_vps = 1;
	context()->RSGetViewports(&num_vps, &app_vp);
	
	// set our own
	setRenderTarget(context());

	context()->RSSetViewports(1, &vp);

	fx_pEye->SetFloatVector((float*)&(camera.getPosVector3()));
	
	// Light // mView, shadow_map, light params
	for (unsigned int i=0; i<lights.size(); ++i)
	{
		fx_lights =			getFX()->GetVariableByName("lights")->GetElement(i);
		fx_tex_shadow_map = getFX()->GetVariableByName("tex_shadow_map")->AsShaderResource();
		
		fx_tex_shadow_map->SetResource(	lights[i]->getShadowMapSRV()	);
		fx_lights->SetRawValue(	(void*)&(lights[i]->getLightStructure()), 0, sizeof(Light_Params)	);
	}
	ID3DX11EffectScalarVariable*	fx_shadow_size = getFX()->GetVariableByName("shadow_size")->AsScalar();
	fx_shadow_size->SetInt(lights[0]->getShadowMapSize());
	fx_num_lights->SetInt(lights.size());
}

void EMReflector::endReflectionDraw()
{
	context()->OMSetRenderTargets(1, &rtv, dsv);
	context()->RSSetViewports(1, &app_vp);

	rtv->Release();
	dsv->Release();
}

XMMATRIX EMReflector::buildViewMatrix(const XMFLOAT4& p, const XMFLOAT4& right, const XMFLOAT4& up, const XMFLOAT4& look) const
{
	XMFLOAT4X4 ret;

	float m41 = -(p.x * right.x + p.y * right.y + p.z * right.z);
	float m42 = -(p.x * up.x    + p.y * up.y    + p.z * up.z);
	float m43 = -(p.x * look.x  + p.y * look.y  + p.z * look.z);

	ret._11 = right.x;		ret._12 = up.x;		ret._13 = look.x;		ret._14 = 0.0f;
	ret._21 = right.y;		ret._22 = up.y;		ret._23 = look.y;		ret._24 = 0.0f;
	ret._31 = right.z;		ret._32 = up.z;		ret._33 = look.z;		ret._34 = 0.0f;
	ret._41 = m41;			ret._42 = m42;		ret._43 = m43;			ret._44 = 1.0f;

	return XMLoadFloat4x4(&ret);
}

void EMReflector::drawReflection(const Mesh* mesh)
{
	// only once
	XMFLOAT4 POS_X = XMFLOAT4( 1.0f,  0.0f,  0.0f, 0.0f);
	XMFLOAT4 POS_Y = XMFLOAT4( 0.0f,  1.0f,  0.0f, 0.0f);
	XMFLOAT4 POS_Z = XMFLOAT4( 0.0f,  0.0f,  1.0f, 0.0f);
	XMFLOAT4 NEG_X = XMFLOAT4(-1.0f,  0.0f,  0.0f, 0.0f);
	XMFLOAT4 NEG_Y = XMFLOAT4( 0.0f, -1.0f,  0.0f, 0.0f);
	XMFLOAT4 NEG_Z = XMFLOAT4( 0.0f,  0.0f, -1.0f, 0.0f);
	XMFLOAT4 pos   = XMFLOAT4(mtxWorld()._41, mtxWorld()._42, mtxWorld()._43, 1.0f);
	

	XMMATRIX proj = XMMatrixPerspectiveFovLH(1.57f, 1.0f, 0.1f, 10.0f);

	
	XMStoreFloat4x4(&mViewProj[0], buildViewMatrix(pos, NEG_Z, POS_Y, POS_X)); 
	XMStoreFloat4x4(&mViewProj[1], buildViewMatrix(pos, POS_Z, POS_Y, NEG_X)); 
	XMStoreFloat4x4(&mViewProj[2], buildViewMatrix(pos, POS_X, NEG_Z, POS_Y)); 
	XMStoreFloat4x4(&mViewProj[3], buildViewMatrix(pos, POS_X, POS_Z, NEG_Y)); 
	XMStoreFloat4x4(&mViewProj[4], buildViewMatrix(pos, POS_X, POS_Y, POS_Z)); 
	XMStoreFloat4x4(&mViewProj[5], buildViewMatrix(pos, NEG_X, POS_Y, NEG_Z)); 
	// /only once

	// 
	ID3DX11EffectMatrixVariable*	fx_m_View = getFX()->GetVariableByName("m_View")->AsMatrix();
	fx_m_View->SetMatrixArray((float*)&mViewProj, 0, 6);

	ID3DX11EffectMatrixVariable* fx_m_Proj = getFX()->GetVariableByName("m_Proj")->AsMatrix();
	fx_m_Proj->SetMatrix((float*)&proj);

	for (int i=0; i<mesh->getNumSubsets(); i++)
	{

		// set World matrix
		fx_m_World->SetMatrix((float*)&(mesh->getWorldMatrix(i)));

		fx_bShadowed->SetBool(mesh->isShadowReceiver());	

		fx_bUsePCSS->SetBool(false);

		ID3DX11EffectScalarVariable*	fx_bLit = getFX()->GetVariableByName("bLit")->AsScalar();
		fx_bLit->SetBool(mesh->isLit());

		mesh->setMaterial(getFX());
	
		mesh->draw(0, getFX(), "Reflection");
	}
}

EMReflector::~EMReflector()
{
	try
	{
	
	}
	catch(...)
	{
		MessageBoxA(0, "exception thrown in  destructor", 0, 0);
	}
}

void EMReflector::init(const char* filename, ID3D11Device* g_device, ID3D11DeviceContext* context)
{
	Mesh::init(filename, g_device, context);

	int w = 512;
	int h = 512;

	// 1. Create Texture
	D3D11_TEXTURE2D_DESC tex_desc;
	ZeroMemory(&tex_desc, sizeof(tex_desc));
	tex_desc.Width = w;
	tex_desc.Height = h;
	tex_desc.MipLevels = 1;
	tex_desc.ArraySize = 6;
	tex_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	tex_desc.SampleDesc.Count = 1; tex_desc.SampleDesc.Quality = 0;
	tex_desc.Usage = D3D11_USAGE_DEFAULT;
	tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE |  D3D11_BIND_RENDER_TARGET;
	tex_desc.CPUAccessFlags = 0;
	tex_desc.MiscFlags = D3D10_RESOURCE_MISC_TEXTURECUBE;

	HRESULT hr = createReflectionTexture2D(g_dev(), tex_desc);
	if (FAILED(hr)) MessageBoxA(0, "Error create EM reflection texture", 0, 0);

	// 2. Create Render Target View
	D3D11_RENDER_TARGET_VIEW_DESC  rtv_desc;
	ZeroMemory(&rtv_desc, sizeof(rtv_desc));
	rtv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
	rtv_desc.Texture2DArray.ArraySize = 6;
	rtv_desc.Texture2DArray.MipSlice = 0;
	rtv_desc.Texture2DArray.FirstArraySlice = 0;


	hr = createRenderTargetView(g_dev(), rtv_desc);
	if (FAILED(hr)) MessageBoxA(0, "Error creating EM reflections rtv", 0, 0);

	// 3. Create Shader Resource View
	D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
	ZeroMemory(&srv_desc, sizeof(srv_desc));
	srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE ; 
	srv_desc.Texture2DArray.ArraySize = 6;
	srv_desc.Texture2DArray.FirstArraySlice = 0;
	srv_desc.Texture2DArray.MipLevels = 1;
	srv_desc.Texture2DArray.MostDetailedMip = 0;
	
	hr = createShaderResourceView(g_dev(), srv_desc);
	if (FAILED(hr)) MessageBoxA(0, "Error creating EM reflections srv", 0, 0);
	
	/////////////////////////////////////////

	// 1. Create Texture
	ZeroMemory(&tex_desc, sizeof(tex_desc));
	tex_desc.Width = w;
	tex_desc.Height = h;
	tex_desc.MipLevels = 1;
	tex_desc.ArraySize = 6;
	tex_desc.Format = DXGI_FORMAT_R32_TYPELESS;
	tex_desc.SampleDesc.Count = 1; tex_desc.SampleDesc.Quality = 0;
	tex_desc.Usage = D3D11_USAGE_DEFAULT;
	tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	tex_desc.CPUAccessFlags = 0; 
	tex_desc.MiscFlags = D3D10_RESOURCE_MISC_TEXTURECUBE;

	hr = createDepthTexture2D(g_dev(), tex_desc);
	if (FAILED(hr)) MessageBoxA(0, "Error creating EM reflections depthtexture", 0, 0);

	// 2. Create Depth stencil View
	D3D11_DEPTH_STENCIL_VIEW_DESC  dsv_desc;
	ZeroMemory(&dsv_desc, sizeof(dsv_desc));
	dsv_desc.Format = DXGI_FORMAT_D32_FLOAT;
	dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
	dsv_desc.Texture2DArray.ArraySize = 6;
	dsv_desc.Texture2DArray.FirstArraySlice = 0;
	dsv_desc.Texture2DArray.MipSlice = 0;

	hr = createDepthStencilView(g_dev(), dsv_desc);
	if (FAILED(hr)) MessageBoxA(0, "Error creating EM reflections dsv", 0, 0);


	// viewport
	ZeroMemory(&vp, sizeof(vp));
	vp.Height = 512;
	vp.Width = 512;
	vp.MaxDepth = 1.0f;


	// fx variables
	fx_m_WorldViewProj	=		getFX()->GetVariableByName("m_WorldViewProj")->AsMatrix();
	fx_m_World			=		getFX()->GetVariableByName("m_World")->AsMatrix();	
	
	fx_tex_shadow_map		= getFX()->GetVariableByName("tex_shadow_map")->AsShaderResource();
	fx_lights				= getFX()->GetVariableByName("lights");
	fx_num_lights			= getFX()->GetVariableByName("num_lights")->AsScalar();
	fx_pEye					= getFX()->GetVariableByName("pEye")->AsVector();
	fx_bShadowed			= getFX()->GetVariableByName("bShadowed")->AsScalar();
	fx_bUsePCSS				= getFX()->GetVariableByName("bUsePCSS")->AsScalar();



}

EMReflector::EMReflector() :	rtv(0), dsv(0), m_Proj(XMFLOAT4X4()), m_reflect(XMFLOAT4X4()),
								fx_m_WorldViewProj(0), fx_m_World(0), fx_tex_shadow_map(0), fx_lights(0), 
								fx_num_lights(0), fx_pEye(0), fx_bShadowed(0), fx_bUsePCSS(0)
{



}